/*
 * Project: DroidAttack - StarView.java
 * Copyright (C) 2010 Jeffrey Rusterholz <dataforger@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */ 

package com.google.code.droidattack;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.Paint.Style;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class StarView extends View {
	private static final String TAG = "DA StarView";

	private Paint mPaint;
    private Typeface mFont = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);

    private Canvas mCanvas; 
	private int mCanvasWidth;
	private int mCanvasHeight;

	// Touch coordinates.
	private int mX;
	private int mY;

	private Bitmap mStarBitmap;
	private TextView mMovesLeft;
	
	// Wormhole lines.
	private DashPathEffect mEffect1 = new DashPathEffect(new float[]{2, 2}, 0);
	private DashPathEffect mEffect2 = new DashPathEffect(new float[]{1, 1}, 0);

	// Game.
	private int mTurnCount;
	
	// Player.
	private int mCurrentPlayer;
	private int mNumberOfMovesLeft;

	// Image, green or red robot.
	private ImageView mPlayerImage;
	
	// Animation variables.
	private boolean mDoAnimStar;
	private int mAnimCounter;
	private int mSelectedStar;
	private Galaxy mGalaxy;

    /**
     * Create a simple handler that we can use to cause animation to happen.  We
     * set ourselves as a target and we can use the sleep()
     * function to cause an update/invalidate to occur at a later date.
     */
    private RefreshHandler mRedrawHandler = new RefreshHandler();

    class RefreshHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            StarView.this.update();
            StarView.this.showMovesLeft();
            StarView.this.invalidate();
        }

        public void sleep(long delayMillis) {
        	this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    };
    
	public StarView(Context context) {
		this(context, null);
	}

	/**
	 * Display text.
	 * @param NewView
	 */
	public void setTextMoves(TextView NewView) {
		mMovesLeft = NewView;
	}

	/**
	 * Set the current player image.
	 * @param img
	 */
	public void setPlayerImage(ImageView img) {
		mPlayerImage = img;
	}

	public void update() {		
		mRedrawHandler.sleep(300);		
	}

	public void update(int sleep) {		
		mRedrawHandler.sleep(sleep);		
	}

	public StarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
	
	public StarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
		
        mPaint = new Paint();
        mPaint.setTextSize(18);
        mPaint.setTypeface(mFont);
        mPaint.setAntiAlias(true);
        
        try {
	        mStarBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star);
        }
        catch (Exception e) {
        	mStarBitmap = null;
        }    
	}	

	public void newGame() {
		mDoAnimStar = false;
		mAnimCounter = -1;
		mSelectedStar = -1;

		mGalaxy = new Galaxy(mCanvasWidth, mCanvasHeight);

		mTurnCount = 0;

		mCurrentPlayer = Star.OWNER_PLAYER1;
        mNumberOfMovesLeft = 3;

        mPlayerImage.setBackgroundResource(R.drawable.robot_green);
        update();
        Log.i(TAG, "New game created.");
	}
	
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);      
        mCanvasWidth = w;
        mCanvasHeight = h;
        
        // Start game now we have the width and height.
        if (mGalaxy == null)
        	newGame();
        else
        	mGalaxy.setStarCoordinates(w, h);
        	update();
    }

    @Override
    public void onDraw(Canvas canvas) { 
    	super.onDraw(canvas);
    	
    	// Something for the layout editor.
        if (mStarBitmap == null) {
        	canvas.drawText("DroidAttack", 40, 80, new Paint());
        	return;
        }
        
    	// Update our canvas reference.
        mCanvas = canvas; 
    	drawGalaxy();  	
    }

    /**
     * Draw the stars in the galaxy.
     */
    private void drawGalaxy() {   	
    	for (Star star: mGalaxy.mStars) {
        
			drawWormholes(star);

			int starX = star.mX + star.xoffset;
			int starY = star.mY + star.yoffset;

    		if (star.owner == Star.OWNER_NONE)
        		mCanvas.drawBitmap(mStarBitmap, starX, starY, null);
    		else { 
    			if (star.owner == Star.OWNER_PLAYER1)
    				mPaint.setColor(0xFFA4C639);
    			else
    				mPaint.setColor(0xFFC60000);
    			    			   			
    			// Home base?
    			if (star.type == Star.TYPE_HOME) {
    				//mPaint.setPathEffect(new DashPathEffect(new float[] {0, 2}, 0) );
    				//mPaint.setStyle(Paint.Style.STROKE);
    				//mCanvas.drawRect(star.mX - 2, star.mY - 2, star.mX + 20, star.mY + 20, mPaint);
    			}
    			    		
    			// Draw rectangle.
    			int recw = 16;
    			int rech = 14;
    			Paint.Style style = Paint.Style.FILL;

    			if (mDoAnimStar) {
    				if (star.id == mSelectedStar) {
    					if (mAnimCounter == 0) mPaint.setColor(0xFF522625);    				
	    				mAnimCounter++;
	    				if (mAnimCounter > 1 ) mAnimCounter = 0;
    				}
    			}
    			
   				mPaint.setStyle(style);
    			mCanvas.drawRect(starX, starY, starX + recw, starY + rech, mPaint);
    			
				// Show occupation progress.
    			int col = mPaint.getColor();
    			mPaint.setColor(Color.BLACK);
    			mCanvas.drawRect(starX, starY + rech, starX + recw, starY + rech + 2, mPaint);

    			mPaint.setColor(col);
    			mPaint.setStyle(Paint.Style.FILL);
    			recw = star.OccupationCount * 4;
    			mCanvas.drawRect(starX, starY + rech, starX + recw, starY + rech + 2, mPaint);
    			
    			// Number of droids present.
    			mPaint.setColor(Color.WHITE);
    			mCanvas.drawText(Long.toString(star.mDroids), starX + 2, starY + 14, mPaint);
    		}
    		
		}
    }

    private void drawWormholes(Star star) {
		int x1, y1, x2, y2;
		int color;
		Paint paint = new Paint();
		DashPathEffect effect;
		
		x1 = star.mX + star.xoffset + 10;
		y1 = star.mY + star.yoffset + 10;
		paint.setStyle(Style.STROKE);
		paint.setStrokeWidth(2.0f);
		
		for (int i = 0; i < star.ConnectedTo.size(); i++) {
			int id = star.ConnectedTo.get(i);
			Star star2 = mGalaxy.findStar(id);
			
			// Only draw wormhole if star id is smaller or its star is selected.
			if (star.id < star2.id | star.id == mSelectedStar) {
				color = Color.CYAN;	
				effect = mEffect1;
				if (mDoAnimStar) {
					if (star.id == mSelectedStar) {
						if (mAnimCounter == 0) {
							if (star.owner == Star.OWNER_PLAYER1)
								color = Color.GREEN;
							else
								color = 0xFFC6A439;
							effect = mEffect2;
						}
					}
				}
				x2 = star2.mX + star2.xoffset + 10;
				y2 = star2.mY + star2.yoffset + 10;			
				paint.setColor(color);
				paint.setPathEffect(effect);
				mCanvas.drawLine(x1, y1, x2, y2, paint);
			}
		}
	}
    
    /**
     * Transport droids from one star to another.
     * @param FromStar
     * @param ToStar
     * @param Number
     */
    private void transportDroids(int FromStar, int ToStar, int Number) {
    	Star from, to;
    	
    	from = mGalaxy.findStar(FromStar);
    	if (from.mDroids < 2) return; // Need at least 2 droids.

    	to = mGalaxy.findStar(ToStar);
    	
    	// Move max. nr of droids?
    	if (Number < 0) {
    		Number = from.mDroids - 1;
    	}

    	// Transfer the droids.
    	from.mDroids -= Number;
    	to.mDroids += Number;
    
    	if (to.owner != from.owner)	{
    		to.OccupationCount = 1;
        	to.owner = from.owner;
    	}

    	decreaseMovesLeft();
    	update(1);
    }

    /**
     * Touchy touchy!
     */
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_DOWN) {
	        mX = (int) event.getX();
	        mY = (int) event.getY();		
			return true;
		}
		
		if (event.getAction() != MotionEvent.ACTION_UP) return false;	
		if (event.getSize() > 4) return false;
		if (mNumberOfMovesLeft == 0) {
			Toast.makeText(getContext(), R.string.No_moves_left, Toast.LENGTH_SHORT).show();
			return false; // No more moves.
		}
		
        mDoAnimStar = false;
        

        int SizeX = mCanvasWidth / mGalaxy.mWidth;
        int SizeY = mCanvasHeight / mGalaxy.mHeight;
        
        // Which column and row pressed?
        int col = mX / SizeX;
        int row = mY / SizeY;
        
        // Calculate the id of the star.
        int id = row * mGalaxy.mWidth + col;        
        Star star = mGalaxy.findStar(id);

        int nTransport = -1; // Nr of droids to transport.
        if (star != null) {
        	if (star.owner == mCurrentPlayer) {
        		if (mSelectedStar < 0) {
		    		// First star selected.
        			mSelectedStar = star.id;
		    		mAnimCounter = 0;
		    		mDoAnimStar = true;
        		} else {
        			// Other star selected.
        			if (mSelectedStar == star.id ) {
        				// Same star then stop animation.
        				mSelectedStar = -1;
        			} else {
            			// Selected star is connected to previous selected star?
            			if (star.ConnectedTo.indexOf(mSelectedStar) >= 0) {
            				// Transfer droid to other star of same player.
            				transportDroids(mSelectedStar, star.id, -1);
            				mSelectedStar = -1;
            			}
        			}
        		}
        	} else {
        		// Other star was already selected?
        		if (mSelectedStar >= 0) {
        			// Selected star is connected to previous selected star?
        			if (star.ConnectedTo.indexOf(mSelectedStar) >= 0) {
        				// Transfer the droids!
        				// TODO: Use number of droids the player chose.
        				if (star.owner == Star.OWNER_NONE) {        					
        					transportDroids(mSelectedStar, star.id, nTransport);
        				} else {
        	        		// The star is owned by the other player.
        	        		// TODO: Attack sequence.
        	        		Star star2 = mGalaxy.findStar(mSelectedStar);
        	        		int attack = star2.mDroids + star2.mDroids / 2; // Attacker.
        	        		int defend = star.mDroids;
        	        		if (attack > defend) {
        	        			star.mDroids = 0;
        	        			transportDroids(mSelectedStar, star.id, nTransport);
        	        		}
        	        		// TODO: Opponent home base conquered: End of Game.
        				}
        			}
    				mSelectedStar = -1;
        		}
        	}
        }
		update(5);
    	return true;
    }
	
	/**
	 * Decrease the number of moves left for the player.
	 */
	private void decreaseMovesLeft() {
		if (mNumberOfMovesLeft > 0) mNumberOfMovesLeft--;
	}
	
	/**
	 * Show the numbers of moves left for the player.
	 */
    private void showMovesLeft() {
    	Resources res = getContext().getResources();
    	CharSequence str = res.getString(R.string.Moves_Left) + 
    	                   " " +
    					   Integer.toString(mNumberOfMovesLeft) +
    	                   " (" + Integer.toString(mTurnCount) + ")";
  		mMovesLeft.setText(str);		
	}

	/**
     * Save game state so that the user does not lose anything
     * if the game process is killed while we are in the 
     * background.
     * 
     * @return a Bundle with this view's state
     */
    public Bundle saveState() {
        Bundle map = new Bundle();

        map.putInt("mTurnCount", Integer.valueOf(mTurnCount));
        map.putInt("mCurrentPlayer", Integer.valueOf(mCurrentPlayer));
        map.putInt("mNumberOfMovesLeft", Integer.valueOf(mNumberOfMovesLeft));

        map.putBoolean("mDoAnimStar", Boolean.valueOf(mDoAnimStar));
        map.putInt("mAnimCounter", Integer.valueOf(mAnimCounter));
        map.putInt("mSelectedStar", Integer.valueOf(mSelectedStar));

        // Serialize Galaxy class.
        map.putSerializable("Galaxy", mGalaxy);
        
        return map;
    }

    /**
     * Restore game state if our process is being relaunched
     * 
     * @param icicle a Bundle containing the game state
     */
    public void restoreState(Bundle icicle) {
    	
    	mTurnCount = icicle.getInt("mTurnCount");
    	mCurrentPlayer = icicle.getInt("mCurrentPlayer");
    	mNumberOfMovesLeft = icicle.getInt("mNumberOfMovesLeft");
    	
    	mDoAnimStar = icicle.getBoolean("mDoAnimStar");
    	mAnimCounter = icicle.getInt("mAnimCounter");
    	mSelectedStar = icicle.getInt("mAnimStar");
    	
    	mGalaxy = (Galaxy) icicle.getSerializable("Galaxy");
    }

	public void setNumberOfMovesLeft(int n) {
		mNumberOfMovesLeft = n;
		
		// Change the player and the robot image.
		if (mCurrentPlayer == Star.OWNER_PLAYER1) {
			mCurrentPlayer = Star.OWNER_PLAYER2;
			mPlayerImage.setBackgroundResource(R.drawable.robot_red);
		} else {
			mCurrentPlayer = Star.OWNER_PLAYER1;
			mPlayerImage.setBackgroundResource(R.drawable.robot_green);
		}
		mGalaxy.increaseOccupation(mCurrentPlayer, mTurnCount);
		mTurnCount++;
		update(0);
	}    
}